package com.goldentrail.neo4j;

import java.net.URI;
import java.net.URISyntaxException;

import javax.ws.rs.core.MediaType;

import org.json.JSONException;
import org.json.JSONObject;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;

/**
 * Java Class to interact with relationships in a remote Neo4j database
 * encapsulating REST calls to the server. use Neo4j.addRelationship() to create
 * a new relationship
 * 
 * @author mikeagun
 */
public class Relationship {
	private URI relUri;
	private static final boolean verboseMode = false;

	/**
	 * Get the URI of the rel in the db that the object points to
	 * 
	 * @return the relUri
	 */
	public URI getURI() {
		return relUri;
	}

	/**
	 * Creates a new relationship object connected to the specified URI. Note
	 * that no error checking is done in this constructor, use one of the
	 * getRelationship functions for error checking.
	 * 
	 * @param relationshipUri
	 *            URI of the db relationship to connect the object to
	 */
	public Relationship(URI relationshipUri) {
		this.relUri = relationshipUri;
	}

	/**
	 * Creates a new relationship object connected to the specified URI. Note
	 * that no error checking is done in this constructor, use one of the
	 * getRelationship functions for error checking.
	 * 
	 * @param relationshipUri
	 *            URI of the db relationship to connect the object to
	 */
	public Relationship(String relationshipUri) {
		try {
			this.relUri = new URI(relationshipUri);
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Retrieves the Relationship with the specified id from the database. Only
	 * use if you know the Neo4j generated id of the relationship
	 * 
	 * @param index
	 *            index of the relationship to be retrieved
	 * @return Relationship object retrieved from database. null if not found
	 */
	public static Relationship getRelationship(long index) {
		WebResource resource = Client.create().resource(
				URIs.SERVER_ROOT_URI + "/db/data/relationship" + index);
		// The get is to ensure that the node actually exists
		ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
				.type(MediaType.APPLICATION_JSON).get(ClientResponse.class);
		// response should be 200
		if (verboseMode)
			System.out.println(String.format(
					"GET to [%s], status code [%d], returned data: ",
					URIs.SERVER_ROOT_URI + "/db/data/relationship" + index,
					response.getStatus()));

		String resp = null;
		try {
			resp = response.getEntity(String.class);
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
		if (verboseMode)
			System.out.println(resp);

		if (response.getStatus() != 200)
			return null;
		try {
			JSONObject json = new JSONObject(resp);
			return new Relationship(json.getString("self"));
		} catch (ClientHandlerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UniformInterfaceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Retrieves the Relationship with the specified URI from the database.
	 * 
	 * @param relationshiUri
	 *            URI of the relationship to be retrieved
	 * @return Relationship object retrieved from database. null if not found
	 */
	public static Relationship getRelationship(String relationshipUri) {
		WebResource resource = Client.create().resource(relationshipUri);
		// The get is to ensure that the node actually exists
		ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
				.type(MediaType.APPLICATION_JSON).get(ClientResponse.class);
		// response should be 200
		if (verboseMode)
			System.out.println(String.format(
					"GET to [%s], status code [%d], returned data: ",
					relationshipUri, response.getStatus()));

		String resp = null;
		try {
			resp = response.getEntity(String.class);
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
		if (verboseMode)
			System.out.println(resp);

		if (response.getStatus() != 200)
			return null;
		try {
			JSONObject json = new JSONObject(resp);
			return new Relationship(json.getString("self"));
		} catch (ClientHandlerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UniformInterfaceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Adds or sets a property on the Relationship.
	 * 
	 * @param name
	 *            name of property to create / set
	 * @param value
	 *            value to give to property
	 */
	public void addProperty(String name, String value) {
		String propertyUri = relUri.toString() + "/properties";
		WebResource resource = Client.create().resource(propertyUri);
		JSONObject json = new JSONObject();
		try {
			json.put(name, value);
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
				.type(MediaType.APPLICATION_JSON).entity(json.toString())
				.put(ClientResponse.class);

		// response should be 200 OK
		if (verboseMode)
			System.out.println(String.format(
					"PUT [%s] to [%s], status code [%d]", json.toString(),
					propertyUri, response.getStatus()));
	}

	/**
	 * Gets the current value of the specified property.
	 * 
	 * @param propertyName
	 *            Name of the property to retrieve
	 * @return value of the property
	 */
	public String getProperty(String propertyName) {
		String propertyUri = relUri.toString() + "/properties/" + propertyName;
		WebResource resource = Client.create().resource(propertyUri);
		ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
				.type(MediaType.APPLICATION_JSON).get(ClientResponse.class);
		// response should be 200 OK i think
		if (verboseMode)
			System.out.println(String.format(
					"GET to [%s], status code [%d], returned data: ",
					propertyUri, response.getStatus()));
		String resp = null;
		try {
			resp = response.getEntity(String.class);
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
		if (verboseMode)
			System.out.println(resp);

		resp = resp.substring(1, resp.length() - 1);
		return resp;
	}

	/**
	 * Gets the Node that the relationship starts from
	 * 
	 * @return Node at start of relationship
	 */
	public Neo4jNode getStartNode() {
		WebResource resource = Client.create().resource(relUri);
		ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
				.type(MediaType.APPLICATION_JSON).get(ClientResponse.class);
		// response should be 200 OK i think
		if (verboseMode)
			System.out.println(String.format(
					"GET to [%s], status code [%d], returned data: ", relUri,
					response.getStatus()));
		String resp = null;
		try {
			resp = response.getEntity(String.class);
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
		if (verboseMode)
			System.out.println(resp);

		if (response.getStatus() != 200)
			return null;
		try {
			JSONObject json = new JSONObject(resp);
			return new Neo4jNode(json.getString("start"));
		} catch (ClientHandlerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UniformInterfaceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Gets the Node that the relationship points to.
	 * 
	 * @return Node at end of relationship
	 */
	public Neo4jNode getEndNode() {
		WebResource resource = Client.create().resource(relUri);
		ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
				.type(MediaType.APPLICATION_JSON).get(ClientResponse.class);
		// response should be 200 OK i think
		if (verboseMode)
			System.out.println(String.format(
					"GET to [%s], status code [%d], returned data: ", relUri,
					response.getStatus()));
		String resp = null;
		try {
			resp = response.getEntity(String.class);
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
		if (verboseMode)
			System.out.println(resp);

		if (response.getStatus() != 200)
			return null;
		try {
			JSONObject json = new JSONObject(resp);
			return new Neo4jNode(json.getString("end"));
		} catch (ClientHandlerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UniformInterfaceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Gets the type of the relationship.
	 * 
	 * @return string containing relationship type
	 */
	public Neo4jNode getType() {
		WebResource resource = Client.create().resource(relUri);
		ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
				.type(MediaType.APPLICATION_JSON).get(ClientResponse.class);
		// response should be 200 OK i think
		if (verboseMode)
			System.out.println(String.format(
					"GET to [%s], status code [%d], returned data: ", relUri,
					response.getStatus()));
		String resp = null;
		try {
			resp = response.getEntity(String.class);
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
		if (verboseMode)
			System.out.println(resp);

		if (response.getStatus() != 200)
			return null;
		try {
			JSONObject json = new JSONObject(resp);
			return new Neo4jNode(json.getString("type"));
		} catch (ClientHandlerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UniformInterfaceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return relUri.toString();
	}
}
